package com.linb.pan.storage.engine.core;

import java.util.List;

import cn.hutool.core.lang.Assert;
import com.linb.pan.cache.core.constants.CacheConstants;
import com.linb.pan.core.exception.BPanBusinessException;
import com.linb.pan.storage.engine.core.context.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;

import java.io.IOException;

public abstract class AbstractStorageEngine implements StorageEngine {

    @Autowired
    private CacheManager cacheManager;

    protected Cache getCache() {
        if (cacheManager == null) {
            throw new BPanBusinessException("The cache manager is empty!");
        }
        return cacheManager.getCache(CacheConstants.B_PAN_CACHE_NAME);
    }

    /**
     * 存储物理文件
     */
    @Override
    public void store(StoreFileContext context) throws IOException {
        checkStoreFileContext(context);
        doStore(context);
    }

    /**
     * 删除物理文件
     */
    @Override
    public void delete(DeleteFileContext context) throws IOException {
        checkDeleteFileContext(context);
        doDelete(context);
    }

    /**
     * 存储物理文件的分片
     */
    @Override
    public void storeChunk(StoreFileChunkContext context) throws IOException {
        //1.校验参数
        checkStoreFileChunkContext(context);
        //2.下沉完成:执行保存文件分片
        doStoreChunk(context);
    }

    /**
     * 合并物理分片文件
     */
    @Override
    public void mergeFile(MergeFileContext context) throws IOException {
        checkMergeFileContext(context);
        doMergeFile(context);
    }

    /**
     * 读取文件，将内容写入到输出流中
     */
    @Override
    public void readFile(ReadFileContext context) throws IOException {
        checkReadFileContext(context);
        doReadFile(context);
    }

    /*====================================abstract method====================================*/

    /**
     * 读取文件内容并写入到输出流中
     */
    protected abstract void doReadFile(ReadFileContext context) throws IOException;

    /**
     * 执行文件分片动作 下沉处理
     */
    protected abstract void doMergeFile(MergeFileContext context) throws IOException;

    /**
     * 执行保存文件分片
     */
    protected abstract void doStoreChunk(StoreFileChunkContext context) throws IOException;

    /**
     * 交给具体的存储引擎类进行存储处理
     */
    protected abstract void doStore(StoreFileContext context) throws IOException;

    /**
     * 交给具体的存储引擎类进行批量删除文件
     */
    protected abstract void doDelete(DeleteFileContext context) throws IOException;

    /*====================================private====================================*/

    /**
     * 校验读取文件上下文参数
     */
    private void checkReadFileContext(ReadFileContext context) {
        Assert.notBlank(context.getRealPath(), "文件存储路径不能为空");
        Assert.notNull(context.getOutputStream(), "文件输出流不能为空");
    }

    /**
     * 合并物理分片文件上下文校验
     */
    private void checkMergeFileContext(MergeFileContext context) {
        Assert.notBlank(context.getFilename(), "文件名不能为空");
        Assert.notBlank(context.getIdentifier(), "文件唯一标识不能为空");
        Assert.notNull(context.getUserId(), "用户id不能为空");
        Assert.notEmpty(context.getRealPathList(), "文件分片列表不能为空");
    }

    /**
     * 存储物理文件的分片校验参数
     */
    private void checkStoreFileChunkContext(StoreFileChunkContext context) {
        Assert.notBlank(context.getFilename(), "文件名不能为空");
        Assert.notBlank(context.getIdentifier(), "文件唯一标识不能为空");
        Assert.notNull(context.getTotalSize(), "文件大小不能为空");
        Assert.notNull(context.getInputStream(), "文件分片不能为空");
        Assert.notNull(context.getTotalChunks(), "文件分片总数不能为空");
        Assert.notNull(context.getChunkNumber(), "文件分片下标不能为空");
        Assert.notNull(context.getCurrentChunkSize(), "文件分片的大小不能为空");
        Assert.notNull(context.getUserId(), "用户id不能为空");
    }

    /**
     * 校验存储文件上下文参数
     */
    private void checkStoreFileContext(StoreFileContext context) {
        Assert.notBlank(context.getFilename(), "文件名不能为空");
        Assert.notNull(context.getTotalSize(), "文件的总大小不能为空");
        Assert.notNull(context.getInputStream(), "物理文件不能为空");
    }

    /**
     * 校验删除文件上下文参数
     */
    private void checkDeleteFileContext(DeleteFileContext context) {
        Assert.notEmpty(context.getRealFilePathList(), "要删除的文件路径列表不能为空");
    }
}
